import { EntityManager, Repository, DataSource } from 'typeorm';
import { Autowired, Clazz } from 'j-spring';
import { EntityList, JpaEntity } from './SpringJpaEntity';
import { springTxManager } from './SpringJpaPostProcessor';
import { Service, Transactional } from './SpringJpaAnnotation';
import {
  FastQuery,
  getUpdateProperty,
  formatFastQuery,
  formatToEntity,
  initFastQuery,
} from './SpringRepositoryUtil';

@Service()
export class SpringOrmDao {
  @Autowired({ clazz: DataSource as Clazz })
  dataSource: DataSource;

  /**
   * 向异步存储空间中获取事务管理器，如果不存在则返回新创建的
   *
   * 删除该方法！所有的
   *
   */
  getEntityManager(): EntityManager {
    return springTxManager.getCurrentEntityManager();
  }

  getTableName(clazz: new () => JpaEntity<any>) {
    return this.getEntityManager().getRepository(clazz).metadata.tableName;
  }

  async getRepositoryByClazz<T extends JpaEntity<any>>(
    clazz: new () => T
  ): Promise<Repository<any>> {
    return this.getEntityManager().getRepository(clazz);
  }

  async getRepository(entity: JpaEntity<any>): Promise<Repository<any>> {
    return this.getRepositoryByClazz((entity as any).constructor);
  }

  private async doOperate(
    entity: EntityList,
    op: (r: Repository<any>, b: any) => Promise<void>
  ) {
    let ds = Array.isArray(entity) ? entity : [entity];
    for (let p of ds) {
      const r = await this.getRepository(p);
      await op(r, p);
    }
  }

  @Transactional()
  save(entity: EntityList): Promise<void> {
    return this.doOperate(entity, (r, b) => r.save(b));
  }

  @Transactional()
  remove(entity: EntityList): Promise<void> {
    return this.doOperate(entity, (r, b) => r.remove(b));
  }

  @Transactional()
  update(entity: EntityList): Promise<void> {
    return this.doOperate(entity, async (r, b) => {
      await r.update({ id: b.id }, getUpdateProperty(b));
    });
  }

  @Transactional()
  async updateProperty<T extends JpaEntity<T>>(
    clazz: new () => T,
    updateField: { [k in keyof T]?: T[k] },
    condition: { [k in keyof T]?: T[k] }
  ) {
    return await this.getEntityManager()
      .getRepository(clazz)
      .update(condition as any, updateField as any);
  }
}

@Service()
export class SpringDao extends SpringOrmDao {
  /**********  用query起的方法 一律是原生sql查询 ********* */

  //查询
  @Transactional()
  async query<T>(query: string, parameters?: any[]): Promise<T[]> {
    const e = this.getEntityManager();
    return await e.query(query, parameters);
  }

  @Transactional()
  async queryEntity<T extends JpaEntity<any>>(query: FastQuery<T>) {
    initFastQuery(query);
    const { isEntity, update } = query;
    const { sql, params } = formatFastQuery(
      this.getTableName(query.clazz),
      query
    );
    const rows = await this.query<T>(sql, params);
    const datas = isEntity
      ? rows.map(d => formatToEntity(query.clazz, d))
      : rows;
    if (update) {
      datas.forEach(update);
      await this.update(datas);
    }
    return datas;
  }

  @Transactional()
  async queryCount<T extends JpaEntity<any>>(
    query: FastQuery<T>
  ): Promise<number> {
    query.starSql = 'select count(*) as count from';
    const [a] = await this.queryEntity(query);
    return Number((a as any).count);
  }
}
